package jbase.project.system.net.http;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import jbase.project.system.core.Sys;
import jbase.project.system.core.common.Console;
import jbase.project.system.core.common.EventHook;
import jbase.project.system.exception.SysException;
import jbase.project.system.io.ContentLengthBlock;
import jbase.project.system.io.EndIdentificationBlock;
import jbase.project.system.io.IOTransfer;
import jbase.project.system.util.HttpUtil;

public class HttpNetService implements HttpConstants, Console {

	private InputStream httpIn = null;
	private OutputStream httpOut = null;
	private Map<String, Object> config = null;
	private EventHook hook;

	protected final static String CONNECT_SUCCESS_RESPONSE_HEADERS = "HTTP/1.1 200 Connection Established\r\n\r\n";

	protected final static int STATUS_READ_REQUEST_HEADER = 0x1;
	protected final static int STATUS_READ_REQUEST_BODY = 0x2;
	protected final static int STATUS_READ_REQUEST_BODY_READ_CONTENT = 0x3;
	protected final static int STATUS_READ_REQUEST_BODY_RD_TRUNKED_HEADER = 0x4;
	protected final static int STATUS_READ_REQUEST_BODY_RD_TRUNKED_BODY = 0x6;
	protected final static int STATUS_READ_REQUEST_BODY_TD_TRUNKED_TRAILER = 7;
	protected final static int STATUS_OVER = 0x5;

	public HttpNetService(EventHook hook, InputStream httpIn, OutputStream httpOut, Map<String, Object> config) {
		this.hook = hook;
		this.httpIn = httpIn;
		this.httpOut = httpOut;
		this.config = config == null ? new HashMap<String, Object>() : config;
		hook.event("object.created", "jbase.project.system.net.http.HttpNetService", this);
	}

	public Map<String, Object> doService() {

		Map<String, Object> result = new HashMap<String, Object>();

		int inCnt = 0;
		byte[] inBuffer = new byte[64 * 1024];

		ByteArrayOutputStream headersData = null;
		ByteArrayOutputStream bodyData = null;

		String headers = null;
		String host = null;
		String method = null;
		String methodsPath = null;
		int contentLength = -1;
		String ver = null;
		String contentType = null;

		EndIdentificationBlock headerBlock = new EndIdentificationBlock(DBCRLF);
		ContentLengthBlock bodyBlock = null;

		int current_status = STATUS_READ_REQUEST_HEADER;
		int transfer_status = 0x1;

		boolean IS_CONTINUE_READ = true;
		try {
			while (IS_CONTINUE_READ) {
				while ((inCnt = httpIn.read(inBuffer)) != -1) {
					for (int i = 0; i < inCnt; i++) {
						byte currentByte = inBuffer[i];
						switch (current_status) {
						case STATUS_READ_REQUEST_HEADER:
							headerBlock.put(currentByte);
							if (headerBlock.haveComplete()) {

								headersData = headerBlock.getByteArrayOutputStream();
								headerBlock = new EndIdentificationBlock(DBCRLF);

								headers = new String(headersData.toByteArray());
								methodsPath = HttpUtil.getRequestMethodsPath(headers);
								method = HttpUtil.getRequestMethod(headers);
								ver = HttpUtil.getRequestVer(headers);
								host = HttpUtil.getRequestHost(headers);
								contentLength = HttpUtil.getContentLength(headers);
								contentType = HttpUtil.getContentType(headers);

								Sys.setThreadVar("request.host", host);
								Sys.setThreadVar("request.headers", headers);
								Sys.setThreadVar("request.methodPath", methodsPath);
								Sys.setThreadVar("request.version", ver);
								Sys.setThreadVar("request.method", method);
								Sys.setThreadVar("request.contentLength", contentLength);
								Sys.setThreadVar("request.contentType", contentType);

								// 未完善，需要根据Keep-Alive来判断是继续下一个生命周期
								if (method.equals(HTTP_GET)) {
									service(headersData.toByteArray(), null, methodsPath, method, ver, host,
											contentType, contentLength);
									current_status = STATUS_READ_REQUEST_HEADER;
								} else if (method.equals(HTTP_POST)) {
									if (contentLength > -1) {
										current_status = STATUS_READ_REQUEST_BODY;
										transfer_status = STATUS_READ_REQUEST_BODY_READ_CONTENT;
										bodyData = new ByteArrayOutputStream();
										bodyBlock = new ContentLengthBlock(contentLength);
									}
								} else if (method.equals(HTTP_PUT)) {

								} else if (method.equals(HTTP_DELETE)) {

								} else if (method.equals(HTTP_CONNECT)) {

								}
							}
							break;
						case STATUS_READ_REQUEST_BODY:
							switch (transfer_status) {
							case STATUS_READ_REQUEST_BODY_READ_CONTENT:
								bodyBlock.put(currentByte);
								if (bodyBlock.haveComplete()) {
									bodyData.write(bodyBlock.getByteArrayOutputStream().toByteArray());
									service(headersData.toByteArray(), bodyData.toByteArray(), methodsPath, method, ver,
											host, contentType, contentLength);
									current_status = STATUS_READ_REQUEST_HEADER;
								}
								break;
							}
							break;
						case STATUS_OVER:
							IS_CONTINUE_READ = false;
							break;
						default:

							break;
						}
					}
				}
				IS_CONTINUE_READ = false;
			}
		} catch (IOException | SysException e) {
			try {
				this.httpIn.close();
			} catch (Exception ex) {
			}
			try {
				this.httpOut.close();
			} catch (Exception ex) {
			}
			hook.event("io.exception", e);
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	private void service(byte[] requestHeaders, byte[] requestBody, String methodPath, String method, String version,
			String host, String contentType, int contentLength) throws SysException {
		Map<String, Object> request = new HashMap<>();

		request.put(HTTP_STREAM_IN, this.httpIn);
		request.put(HTTP_STREAM_OUT, this.httpOut);

		request.put(HTTP_REQUEST_FIELD_HEADERS, requestHeaders);
		request.put(HTTP_REQUEST_FIELD_METHOD_PATH, methodPath);
		request.put(HTTP_REQUEST_FIELD_METHOD, method);
		request.put(HTTP_REQUEST_FIELD_HOST, host);
		request.put(HTTP_REQUEST_FIELD_VERSION, version);
		request.put(HTTP_REQUEST_FIELD_CONTENT_LENGTH, contentLength);
		request.put(HTTP_REQUEST_FIELD_CONTENT_TYPE, contentType);
		request.put(HTTP_REQUEST_FIELD_BODY, requestBody);

		request.put("config", this.config);
		if (method.equals(HTTP_GET)) {
			hook.event(HTTP_REQUEST_DELEGATE_EVENT_GET, request);
		} else if (method.equals(HTTP_POST)) {
			hook.event(HTTP_REQUEST_DELEGATE_EVENT_POST, request);
		}
	}

	@Override
	public <T, R> R executeCommand(String command, T... objects) {
		return null;
	}

}
